/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "database.h"
#include <cstdarg>
#include <algorithm>
#include "node/log/logsystem.h"
#include "node/log/logger.h"
#include "node/util/string.h"
#include "node/system/lockguard.h"
#include "node/db/sql_delaythread.h"
using namespace ::node::system;
using namespace ::node::util;

namespace node {
    namespace db {

        Database::Database(void)
            : allow_async_transactions_(true)
            , conn_()
            , async_conn_()

            , stmt_registry_()
            , stmt_registry_lock_()
            , stmt_index_(0)

            , result_queue_(new SqlResultQueue)

            , delay_thread_()
        {

        }

        Database::~Database(void)
        {
            if (result_queue_) {
                delete result_queue_;
            }
        }

        void Database::escapeString(::std::string &sql)
        {

        }

        ::node::db::ResultSet *Database::pquery(const char *sql, ...)
        {
            char lsql[MAX_QUERY_LENGTH];

            std::va_list varg;
            va_start(varg, sql);
            int ret = string::vsnprintf(lsql, MAX_QUERY_LENGTH, sql, varg);
            va_end(varg);

            if (ret == -1)
            {
                NODE_LOGGER("main").error("SQL Query truncated (and not execute) for format: %s\n", sql);
                return NULL;
            }

            ::node::db::ResultSet *rs = conn_->query(lsql);
            if (!rs && conn_->error()) {
				NODE_LOGGER("main").error("[Database::Query] %s\n", conn_->error_message().c_str());
            }
            return rs;
        }

        bool Database::pexecute(const char *sql, ...)
        {
            char lsql[MAX_QUERY_LENGTH];

            std::va_list varg;
            va_start(varg, sql);
            int ret = string::vsnprintf(lsql, MAX_QUERY_LENGTH, sql, varg);
            va_end(varg);

            if (ret == -1)
            {
				NODE_LOGGER("main").error("SQL Query truncated (and not execute) for format: %s\n", sql);
                return false;
            }

            if (!conn_->execute(lsql)) {
				NODE_LOGGER("main").error("[Database::Execute] %s\n", conn_->error_message().c_str());
                return false;
            }

            return true;
        }

        SqlStatement Database::createStatement(SqlStatementID& id, const char *format)
        {
            int nId = -1;
            if (!id.initialized())
            {
                std::string szFmt(format);
                int nParams = std::count(szFmt.begin(), szFmt.end(), '?');
                NODE_GUARD_LOCK_ASSERT(stmt_registry_lock_);
                PreparedStmtRegistry::const_iterator iter = stmt_registry_.find(szFmt);
                if (iter == stmt_registry_.end())
                {
                    nId = ++stmt_index_;
                    stmt_registry_[szFmt] = nId;
                }
                else
                { nId = iter->second; }

                id.init(nId, nParams);
            }

            return SqlStatement(id, *this);
        }

        bool Database::executeStmt(const SqlStatementID& id, SqlStmtParameters* params)
        {
            if (!async_conn_)
            { return false; }

            if (!allow_async_transactions_)
            { return directExecuteStmt(id, params); }
            delay_thread_->delay(new SqlPreparedRequest(id.id(), params));
            return true;
        }

        bool Database::directExecuteStmt(const SqlStatementID& id, SqlStmtParameters* params)
        {
            NODE_ASSERT(params);
            std::auto_ptr<SqlStmtParameters> p(params);
            Connection::Lock guard(async_conn_.get());
            return guard->executeStmt(id.id(), *params);
        }

        std::string Database::getStmtString(const int stmtId) const
        {
            NODE_GUARD_LOCK_ASSERT_RETURN_VAR(stmt_registry_lock_, std::string());

            if (stmtId == -1 || stmtId > stmt_index_)
            { return std::string(); }

            PreparedStmtRegistry::const_iterator iter_last = stmt_registry_.end();
            for (PreparedStmtRegistry::const_iterator iter = stmt_registry_.begin(); iter != iter_last; ++iter)
            {
                if (iter->second == stmtId)
                { return iter->first; }
            }

            return std::string();
        }

        void Database::processResultQueue(void)
        {
            if (result_queue_) {
                result_queue_->update();
            }
        }
    }
}